home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- header.c
-
- This module exports several utility functions for working with news
- and mail headers.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <string.h>
- #include <stdio.h>
- #include <ctype.h>
-
- #include "glob.h"
- #include "header.h"
- #include "menus.h"
- #include "newswatcher.h"
- #include "strutil.h"
- #include "net.h"
- #include "memutil.h"
- #include "ic.h"
-
-
-
- typedef enum ELineBreakMapping {
- kMapToSpace, /* map line breaks to space */
- kMapToComma, /* map line breaks to comma */
- kMapToCR, /* map line breaks to CR */
- kNoMapCR /* no line break mapping */
- } ELineBreakMapping;
-
-
-
- /*----------------------------------------------------------------------------
- LocateHeaderLine
-
- Locate a header line.
-
- Entry: text = handle to header text.
- hdrEnd = length of header text.
- key = C-format header to locate, not including
- the terminating ":".
-
- Exit: function result = true if header found, else false.
- *start = offset in text of start of header contents.
- *len = length of header contents.
-
- This function only returns the first part of "folded" header lines.
- ----------------------------------------------------------------------------*/
-
- static Boolean LocateHeaderLine (Handle text, long hdrEnd, char *key, long *start, long *len)
- {
- long keyLen;
- char *p, *pEnd, *q;
-
- keyLen = strlen(key);
- p = *text;
- pEnd = *text + hdrEnd;
- while (p < pEnd) {
- if (MyStrNEqual(p, key, keyLen)) {
- p += keyLen;
- while (p < pEnd && isLWSP(*p)) p++;
- if (*p == ':') {
- p++;
- while (p < pEnd && isLWSP(*p)) p++;
- q = p;
- while (q < pEnd && *q != CR) q++;
- q--;
- while (q >= p && isLWSP(*q)) q--;
- q++;
- *start = p - *text;
- *len = q - p;
- return true;
- }
- }
- while (p < pEnd && *p != CR) p++;
- p++;
- }
- return false;
- }
-
-
-
- /*----------------------------------------------------------------------------
- LocateArticleHeaderLine
-
- Locate a message header line in an article.
-
- Entry: text = handle to article text.
- key = C-format header to locate, not including
- the terminating ":".
-
- Exit: function result = true if header found, else false.
- *start = offset in text of start of header contents.
- *len = length of header contents.
-
- This function only returns the first part of "folded" header lines.
- The caller should call LocateContinuationHeaderLine below to locate
- the continuation lines.
- ----------------------------------------------------------------------------*/
-
- static Boolean LocateArticleHeaderLine (Handle text, char *key, long *start, long *len)
- {
- long hdrEnd;
-
- hdrEnd = Munger(text, 0, CRCR, 2, nil, 0);
- if (hdrEnd < 0) hdrEnd = MyGetHandleSize(text);
- return LocateHeaderLine(text, hdrEnd, key, start, len);
- }
-
-
-
- /*----------------------------------------------------------------------------
- LocateContinuationHeaderLine
-
- Locate a continuation message header line in an article.
-
- Entry: text = handle to article text.
- *start = offset in text of first character following end of
- previous line in "folded" header line.
-
- Exit: function result = true if continuation header line found, else false.
- *start = offset in text of start of continuation header line.
- *len = length of continuation header line.
- ----------------------------------------------------------------------------*/
-
- static Boolean LocateContinuationHeaderLine (Handle text, long *start, long *len)
- {
- char *p, *pEnd, *q;
-
- p = *text + *start;
- pEnd = *text + MyGetHandleSize(text);
- while (p < pEnd && isLWSP(*p)) p++;
- if (p >= pEnd || *p != CR) return false;
- p++;
- if (p >= pEnd || !isLWSP(*p)) return false;
- while (p < pEnd && isLWSP(*p)) p++;
- q = p;
- while (q < pEnd && *q != CR) q++;
- q--;
- while (q >= p && isLWSP(*q)) q--;
- q++;
- *start = p - *text;
- *len = q - p;
- return true;
- }
-
-
-
-
- /*----------------------------------------------------------------------------
- MakePathHeader
-
- Make a "Path" header line.
-
- Exit: function result = error code.
- path = C-format "Path" header line.
- ----------------------------------------------------------------------------*/
-
- static OSErr MakePathHeader (char path[261])
- {
- OSErr err = noErr;
-
- err = NetGetMyName(path);
- if (err == noErr) {
- strcat(path, "!user");
- } else if (err == userCanceledErr) {
- return err;
- } else {
- strcpy(path, "NewsWatcher!user");
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeDateHeader
-
- Make a "Date" header line.
-
- Exit: date = C-format "Date" header line. Empty string if can't get
- machine location.
- ----------------------------------------------------------------------------*/
-
- static void MakeDateHeader (CStr255 date)
- {
- static char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- static char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
-
- DateTimeRec dtRec;
- MachineLocation loc;
- long gmtDelta;
- char gmtSign;
- short gmtHours;
- short gmtMinutes;
-
- ReadLocation(&loc);
- *date = 0;
- if (loc.latitude == 0 && loc.longitude == 0 && loc.u.gmtDelta == 0) return;
- gmtDelta = loc.u.gmtDelta & 0x00ffffff;
- if ((gmtDelta >> 23) & 1) gmtDelta |= 0xff000000;
- if (gmtDelta < 0) {
- gmtSign = '-';
- gmtDelta = -gmtDelta;
- } else {
- gmtSign = '+';
- }
- gmtHours = gmtDelta / 3600;
- gmtMinutes = (gmtDelta % 3600) / 60;
- GetTime(&dtRec);
- sprintf(date, "%s, %.2d %s %.4d %.2d:%.2d:%.2d %c%.2d%.2d",
- days[dtRec.dayOfWeek - 1],
- dtRec.day,
- months[dtRec.month - 1],
- dtRec.year,
- dtRec.hour,
- dtRec.minute,
- dtRec.second,
- gmtSign,
- gmtHours,
- gmtMinutes
- );
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeFromHeader
-
- Make a "From" header line.
-
- Exit: from = C-format "From" header line.
- ----------------------------------------------------------------------------*/
-
- void MakeFromHeader (char from[514])
- {
- MyICReadSharedPrefs(kICRealName);
- MyICReadSharedPrefs(kICEmail);
-
- if (*gPrefs.fullName == 0) {
- strcpy(from, gPrefs.emailAddress);
- } else {
- sprintf(from, "%s (%s)", gPrefs.emailAddress, gPrefs.fullName);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeMsgIdHeader
-
- Make a "Message-ID" header line.
-
- Exit: function result = error code.
- id = message id header line, or empty string if error.
- ----------------------------------------------------------------------------*/
-
- static OSErr MakeMsgIdHeader (char id[512])
- {
- static DateTimeRec prevDtRec = {0, 0, 0, 0, 0, 0, 0};
- static short uniqueWithinSecond = 0;
- DateTimeRec dtRec;
- CStr255 hostName;
- OSErr err = noErr;
- char *p;
- short len;
-
- MyICReadSharedPrefs(kICEmail);
-
- *id = 0;
- if (*gPrefs.emailAddress == 0) return noErr;
- err = NetGetMyName(hostName);
- if (err != noErr) {
- if (err == userCanceledErr) return err;
- err = NetGetMyAddrStr(hostName);
- if (err != noErr) return err;
- }
- if (*hostName == 0) return noErr;
- for (p = gPrefs.emailAddress; *p != 0 && *p != '@' && *p != '%' &&
- *p != ' ' && *p != '<' && *p != '>' && *p != ','; p++) /* do nothing */;
- len = p - gPrefs.emailAddress;
- GetTime(&dtRec);
- if (prevDtRec.day == dtRec.day && prevDtRec.month == dtRec.month &&
- prevDtRec.year == dtRec.year && prevDtRec.hour == dtRec.hour &&
- prevDtRec.minute == dtRec.minute && prevDtRec.second == dtRec.second)
- {
- uniqueWithinSecond++;
- } else {
- prevDtRec = dtRec;
- uniqueWithinSecond = 1;
- }
- sprintf(id, "<%.*s-%.2d%.2d%.2d%.2d%.2d%.2d%.4d@%s>",
- len, gPrefs.emailAddress, dtRec.day, dtRec.month,
- dtRec.year%100, dtRec.hour, dtRec.minute, dtRec.second,
- uniqueWithinSecond, hostName);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- AddHeader
-
- Add a single header line to a header under construction.
-
- Entry: key = C-format header name string, not including the
- terminating colon.
- hdrContents = pointer to header contents. Nil or empty if none.
- hdrContentsLen = length of header contents.
- hdr = handle to header under construction.
- *hdrNext = index in header to store next line.
- stripWhiteSpace = true to strip white space from header contents.
- lineBreakMapping = line break mapping in header contents.
-
- Exit: function result = error code.
- *hdrNext updated.
- ----------------------------------------------------------------------------*/
-
- static OSErr AddHeader (char *key, char *hdrContents, long hdrContentsLen,
- Handle hdr, long *hdrNext, Boolean stripWhiteSpace,
- ELineBreakMapping lineBreakMapping)
- {
- long next, size, len, keyLen;
- OSErr err = noErr;
- char *start, *end, *p, *q;
-
- if (hdrContents == nil) return noErr;
- start = hdrContents;
- end = start + hdrContentsLen - 1;
- while (start <= end && isLWSPorCR(*start)) start++;
- while (start <= end && isLWSPorCR(*end)) end--;
- hdrContentsLen = end - start + 1;
- if (hdrContentsLen <= 0) return noErr;
-
- keyLen = strlen(key);
- len = keyLen + hdrContentsLen + 3;
-
- next = *hdrNext;
- size = MyGetHandleSize(hdr);
- if (next + len > size) {
- err = MySetHandleSize(hdr, next + len + 1000);
- if (err != noErr) return err;
- }
-
- q = *hdr + next;
- BlockMoveData(key, q, keyLen);
- q += keyLen;
- BlockMoveData(": ", q, 2);
- q += 2;
-
- p = start;
- while (p <= end) {
- if (stripWhiteSpace && isLWSP(*p)) {
- p++;
- } else if (*p == CR && lineBreakMapping != kNoMapCR) {
- q--;
- while (q >= *hdr && isLWSPorCR(*q)) q--;
- q++;
- p++;
- while (p <= end && isLWSPorCR(*p)) p++;
- switch (lineBreakMapping) {
- case kMapToSpace:
- *q++ = ' ';
- break;
- case kMapToComma:
- *q++ = ',';
- break;
- case kMapToCR:
- *q++ = CR;
- break;
- }
- } else {
- *q++ = *p++;
- }
- }
- *q++ = CR;
-
- *hdrNext = q - *hdr;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- AddHeaderCString
-
- Add a single C-format header line to a header under construction.
-
- Entry: key = C-format header name string, not including the
- terminating colon.
- hdrContents = C-format header contents. Nil or empty if none.
- hdr = handle to header under construction.
- *hdrNext = index in header to store next line.
- stripWhiteSpace = true to strip white space from header contents.
- lineBreakMapping = line break mapping in header contents.
-
- Exit: function result = error code.
- *hdrNext updated.
- ----------------------------------------------------------------------------*/
-
- static OSErr AddHeaderCString (char *key, char *hdrContents, Handle hdr, long *hdrNext,
- Boolean stripWhiteSpace, ELineBreakMapping lineBreakMapping)
- {
- return AddHeader(key, hdrContents, strlen(hdrContents), hdr, hdrNext,
- stripWhiteSpace, lineBreakMapping);
- }
-
-
-
- /*----------------------------------------------------------------------------
- AddHeaderHandle
-
- Add a single header line contained in a relocatable block to a header
- under construction.
-
- Entry: key = C-format header name string, not including the
- terminating colon.
- hdrContents = handle to header contents. Nil or empty if none.
- hdr = handle to header under construction.
- *hdrNext = index in header to store next line.
- stripWhiteSpace = true to strip white space from header contents.
- lineBreakMapping = line break mapping in header contents.
-
- Exit: function result = error code.
- *hdrNext updated.
- ----------------------------------------------------------------------------*/
-
- static OSErr AddHeaderHandle (char *key, Handle hdrContents, Handle hdr, long *hdrNext,
- Boolean stripWhiteSpace, ELineBreakMapping lineBreakMapping)
- {
- OSErr err = noErr;
- char state;
-
- if (hdrContents == nil) return noErr;
- state = MyHGetState(hdrContents);
- MyHLock(hdrContents);
- err = AddHeader(key, *hdrContents, MyGetHandleSize(hdrContents), hdr, hdrNext,
- stripWhiteSpace, lineBreakMapping);
- MyHSetState(hdrContents, state);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- AddExtraHeaderLines
-
- Add extra header lines, overriding any earlier ones.
-
- Entry: extras = handle to extra header lines. Nil or empty if none.
- hdr = handle to header under construction.
- *hdrNext = index in header to store next line.
-
- Exit: function result = error code.
- *hdrNext updated.
- ----------------------------------------------------------------------------*/
-
- static OSErr AddExtraHeaderLines (Handle extras, Handle hdr, long *hdrNext)
- {
- long keyLen, contentLen;
- OSErr err = noErr;
- char *p, *pEnd, *q, *r, *s;
- CStr255 key;
- long start, len;
- char state;
-
- state = MyHGetState(extras);
-
- if (extras == nil) return noErr;
- MyHLock(extras);
- p = *extras;
- pEnd = p + MyGetHandleSize(extras);
- while (p < pEnd) {
- q = p;
- while (q < pEnd && *q != CR && *q != ':') q++;
- if (q >= pEnd || *q == CR) {
- p = q+1;
- continue;
- }
- s = r = q+1;
- while (true) {
- while (r < pEnd && *r != CR) r++;
- if (r >= pEnd) break;
- if (!isLWSP(*(r+1))) break;
- r++;
- }
- q--;
- while (q >= p && isLWSP(*q)) q--;
- q++;
- if (p < q) {
- keyLen = q - p;
- if (keyLen < 256) {
- BlockMoveData(p, key, keyLen);
- key[keyLen] = 0;
- while (s < pEnd && isLWSP(*s)) s++;
- contentLen = r - s;
- if (contentLen > 0) {
- if (LocateHeaderLine(hdr, *hdrNext, key, &start, &len)) {
- Munger(hdr, start, nil, len, s, contentLen);
- err = MemError();
- if (err != noErr) goto exit;
- *hdrNext += contentLen - len;
- } else {
- err = AddHeader(key, s, contentLen, hdr, hdrNext, false, kNoMapCR);
- if (err != noErr) goto exit;
- }
- }
- }
- }
- p = r+1;
- }
-
- exit:
-
- MyHSetState(extras, state);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- HeaderLineIsEmpty
-
- Check for an empty header line.
-
- Entry: hdr = handle to header line.
-
- Exit: function result = true if header line is empty (nil, or consists
- of only space, CR, and tab characters).
- ----------------------------------------------------------------------------*/
-
- Boolean HeaderLineIsEmpty (Handle hdr)
- {
- char *p, *pEnd;
-
- if (hdr == nil) return true;
- p = *hdr;
- pEnd = p + MyGetHandleSize(hdr);
- while (p < pEnd && isLWSPorCR(*p)) p++;
- return p >= pEnd;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeNewsHeader
-
- Make a news article header.
-
- Entry: newsgroups = Handle to "Newsgroups" header contents.
- subject = Handle to "Subject" header contents.
- replyto = Handle to "Reply-To" header contents. Nil or empty if none.
- followupto = Handle to "Followup-To" header contents. Nil or empty if none.
- keywords = Handle to "Keywords" header contents. Nil or empty if none.
- distribution = Handle to "Distribution" header contents. Nil or empty if none.
- extras = Handle to extra header lines. Nil or empty if none.
- control = Handle to "Control" header contents. Nil or empty if none.
- references = Handle to "References" header contents. Nil or empty if none.
-
- Exit: function result = error code.
- header = handle to header.
-
- The constructed header includes a blank line at the end (CRCR).
- ----------------------------------------------------------------------------*/
-
- OSErr MakeNewsHeader (Handle newsgroups, Handle subject, Handle replyto,
- Handle followupto, Handle keywords, Handle distribution, Handle extras,
- Handle control, Handle references, Handle *header)
- {
- Handle hdr;
- OSErr err = noErr;
- long hdrNext;
- CStr255 date;
- char path[261];
- char from[514];
- char messageid[512];
-
- MyICReadSharedPrefs(kICOrganization);
-
- err = MyNewHandle(1000, &hdr);
- if (err != noErr) return err;
- hdrNext = 0;
-
- err = MakePathHeader(path);
- if (err != noErr) goto exit;
- MakeDateHeader(date);
- MakeFromHeader(from);
- err = MakeMsgIdHeader(messageid);
- if (err != noErr) goto exit;
-
- err = AddHeaderCString("Path", path, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderCString("Date", date, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderCString("From", from, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Newsgroups", newsgroups, hdr, &hdrNext, true, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Followup-To", followupto, hdr, &hdrNext, true, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Reply-To", replyto, hdr, &hdrNext, false, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Distribution", distribution, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Keywords", keywords, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Subject", subject, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderCString("Message-ID", messageid, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("References", references, hdr, &hdrNext, false, kNoMapCR);
- if (err != noErr) goto exit;
- err = AddHeaderCString("Organization", gPrefs.organization, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Control", control, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddExtraHeaderLines(extras, hdr, &hdrNext);
- if (err != noErr) goto exit;
-
- err = MySetHandleSize(hdr, hdrNext+1);
- if (err != noErr) goto exit;
- *(*hdr + hdrNext) = CR;
-
- *header = hdr;
- return noErr;
-
- exit:
-
- MyDisposeHandle(hdr);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MakeMailHeader
-
- Make a mail message header.
-
- Entry: subject = Handle to "Subject" header contents.
- to = Handle to "To" header contents. Nil or empty if none.
- cc = Handle to "Cc" header contents. Nil or empty if none.
- bcc = Handle to "Bcc" header contents. Nil or empty if none.
- from = Handle to "From" header contents. Nil or empty to use
- gPrefs.emailAddress (gPrefs.fullName).
- copySelf = true to include self in "bcc" header line, or in "to"
- header line if to, cc, and bcc are all empty.
- replyto = Handle to "Reply-To" header contents. Nil or empty if none.
- keywords = Handle to "Keywords" header contents. Nil or empty if none.
- newsgroups = Handle to "Newsgroups" header contents. Nil or empty if none.
- followupto = Handle to "Followup-To" header contents. Nil or empty if none.
- distribution = Handle to "Distribution" header contents. Nil or empty if none.
- extras = Handle to extra header lines. Nil or empty if none.
- references = Handle to "References" header contents. Nil or empty if none.
-
- Exit: function result = error code.
- header = handle to header.
-
- The constructed header includes a blank line at the end (CRCR).
- ----------------------------------------------------------------------------*/
-
- OSErr MakeMailHeader (Handle subject, Handle to, Handle cc, Handle bcc, Handle from,
- Boolean copySelf, Handle replyto, Handle keywords, Handle extras,
- Handle newsgroups, Handle followupto, Handle distribution,
- Handle references, Handle *header)
- {
- Handle hdr = nil;
- Handle xbcc = nil;
- Handle xto = nil;
- Boolean disposeXbcc = false;
- Boolean disposeXto = false;
- OSErr err = noErr;
- long hdrNext;
- CStr255 date;
- char fromHdr[514];
- long bccLen, selfLen, xbccLen;
- char *q;
-
- MyICReadSharedPrefs(kICOrganization);
- MyICReadSharedPrefs(kICEmail);
-
- err = MyNewHandle(1000, &hdr);
- if (err != noErr) return err;
- hdrNext = 0;
-
- MakeDateHeader(date);
-
- xto = to;
- xbcc = bcc;
- if (copySelf) {
- selfLen = strlen(gPrefs.emailAddress);
- if (HeaderLineIsEmpty(to) && HeaderLineIsEmpty(cc)) {
- err = MyNewHandle(selfLen, &xto);
- if (err != noErr) goto exit;
- disposeXto = true;
- BlockMoveData(gPrefs.emailAddress, *xto, selfLen);
- } else {
- bccLen = bcc == nil ? 0 : MyGetHandleSize(bcc);
- xbccLen = bccLen + selfLen;
- if (bcc != nil) xbccLen++;
- err = MyNewHandle(xbccLen, &xbcc);
- if (err != noErr) goto exit;
- disposeXbcc = true;
- q = *xbcc;
- if (bcc != nil) {
- BlockMoveData(*bcc, *xbcc, bccLen);
- q += bccLen;
- *q++ = ',';
- }
- BlockMoveData(gPrefs.emailAddress, q, selfLen);
- }
- }
-
- err = AddHeaderCString("Date", date, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- if (HeaderLineIsEmpty(from)) {
- MakeFromHeader(fromHdr);
- err = AddHeaderCString("From", fromHdr, hdr, &hdrNext, false, kMapToSpace);
- } else {
- err = AddHeaderHandle("From", from, hdr, &hdrNext, false, kMapToSpace);
- }
- if (err != noErr) goto exit;
- err = AddHeaderHandle("To", xto, hdr, &hdrNext, false, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Cc", cc, hdr, &hdrNext, false, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Bcc", xbcc, hdr, &hdrNext, false, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Reply-To", replyto, hdr, &hdrNext, false, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Subject", subject, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Keywords", keywords, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Newsgroups", newsgroups, hdr, &hdrNext, true, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Followup-To", followupto, hdr, &hdrNext, false, kMapToComma);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("Distribution", distribution, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddHeaderHandle("References", references, hdr, &hdrNext, false, kNoMapCR);
- if (err != noErr) goto exit;
- err = AddHeaderCString("Organization", gPrefs.organization, hdr, &hdrNext, false, kMapToSpace);
- if (err != noErr) goto exit;
- err = AddExtraHeaderLines(extras, hdr, &hdrNext);
- if (err != noErr) goto exit;
-
- err = MySetHandleSize(hdr, hdrNext+1);
- if (err != noErr) goto exit;
- *(*hdr + hdrNext) = CR;
-
- if (disposeXto) MyDisposeHandle(xto);
- if (disposeXbcc) MyDisposeHandle(xbcc);
-
- *header = hdr;
- return noErr;
-
- exit:
-
- MyDisposeHandle(hdr);
- if (disposeXto) MyDisposeHandle(xto);
- if (disposeXbcc) MyDisposeHandle(xbcc);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FindHeaderCString
-
- Find and extract a message header from an article and return it as a
- C-format string.
-
- Entry: text = handle to article text.
- key = C-format header to locate, not including
- the terminating ":".
- maxLen = maximum length of returned header contents, including
- C-format terminating 0 byte.
-
- Exit: function result = true if header found, else false.
- contents = extracted C-format header contents, with leading and
- trailing white space deleted.
-
- If the header content string is longer than the maximum length, it is
- truncated to the maximum length.
- ----------------------------------------------------------------------------*/
-
- Boolean FindHeaderCString (Handle text, char *key, char *contents,
- long maxLength)
- {
- long start, len;
- char *q;
-
- *contents = 0;
- q = contents;
- if (!LocateArticleHeaderLine(text, key, &start, &len)) return false;
- while (true) {
- if (len >= maxLength) len = maxLength - 1;
- BlockMoveData(*text + start, q, len);
- start += len;
- q += len;
- maxLength -= len;
- if (maxLength <= 1) break;
- if (!LocateContinuationHeaderLine(text, &start, &len)) break;
- *q++ = ' ';
- maxLength--;
- }
- *q = 0;
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FindHeaderHandle
-
- Find and extract a message header from an article and return it in a
- relocatable block.
-
- Entry: text = handle to article text.
- key = C-format header to locate, not including
- the terminating ":".
-
- Exit: function result = error code.
- contents = handle to extracted header contents, with leading and
- trailing white space deleted, or nil if header not found.
- ----------------------------------------------------------------------------*/
-
- OSErr FindHeaderHandle (Handle text, char *key, Handle *contents)
- {
- long start, firstLineStart, firstLineLen, len, totalLen;
- Handle h;
- char *q;
- OSErr err = noErr;
-
- *contents = nil;
- if (!LocateArticleHeaderLine(text, key, &start, &len)) return noErr;
- totalLen = 0;
- firstLineStart = start;
- firstLineLen = len;
- while (true) {
- totalLen += len;
- start += len;
- if (!LocateContinuationHeaderLine(text, &start, &len)) break;
- totalLen += 1;
- }
- err = MyNewHandle(totalLen, &h);
- if (err != noErr) return err;
- MyHLock(h);
- q = *h;
- start = firstLineStart;
- len = firstLineLen;
- while (true) {
- BlockMoveData(*text + start, q, len);
- start += len;
- q += len;
- if (!LocateContinuationHeaderLine(text, &start, &len)) break;
- *q++ = ' ';
- }
- MyHUnlock(h);
- *contents = h;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FormatAuthorName
-
- Format an author name.
-
- Entry: name = C-format "From" header contents.
-
- Exit: name = formatted name.
-
- If the "From" line is in the form "address (name)", "name" is returned.
- If the "From" line is in the form "name <address>", "name" is returned.
- Otherwise the "From" line is unchanged.
-
- Note that the name is formatted in place, destroying the original
- string, and that the formatted name is always shorter or the same length
- as the orginal string.
- ----------------------------------------------------------------------------*/
-
- void FormatAuthorName (char *name)
- {
- char *p, *start, *end;
- short len, parenLevel;
-
- start = name;
- end = name + strlen(name) - 1;
-
- p = name;
- while (*p != 0 && *p != '(' && *p != '<') p++;
- if (*p == '(') {
- /* format = "address (name)" */
- p++;
- start = p;
- parenLevel = 1;
- while (*p != 0) {
- if (*p == '(') {
- parenLevel++;
- } else if (*p == ')') {
- parenLevel--;
- if (parenLevel == 0) break;
- }
- p++;
- }
- end = p-1;
- } else if (*p == '<') {
- /* format = "name <address>" */
- end = p-1;
- } else {
- return;
- }
- if (start <= end) {
- while (start < end && (isLWSP(*start) || *start == '"')) start++;
- while (start < end && (isLWSP(*end) || *end == '"')) end--;
- len = end - start + 1;
- if (len > 0) {
- BlockMoveData(start, name, len);
- name[len] = 0;
- }
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- FindBody
-
- Find the beginning of the body of an article.
-
- Entry: text = handle to article text.
-
- Exit: function result = offset in article of first character of
- article body, or length of article if article has no body.
- ----------------------------------------------------------------------------*/
-
- long FindBody (Handle text)
- {
- char *p, *pEnd;
- long length;
-
- length = MyGetHandleSize(text);
- p = *text;
- pEnd = p + length;
- while (p < pEnd) {
- if (*p == CR && *(p+1) == CR) break;
- p++;
- }
- p += 2;
- while (p < pEnd && *p == CR) p++;
- return p < pEnd ? p-*text : length;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DeleteHeaderLine
-
- Delete a header line.
-
- Entry: text = handle to text.
- key = keyword of header line to delete.
- ----------------------------------------------------------------------------*/
-
- void DeleteHeaderLine (Handle text, char *key)
- {
- long textLen, firstStart, len, start;
-
- textLen = MyGetHandleSize(text);
- if (!LocateHeaderLine(text, textLen, key, &firstStart, &len)) return;
- start = firstStart + len;
- while (LocateContinuationHeaderLine(text, &start, &len)) start = start + len;
- firstStart--;
- while (firstStart >= 0 && *(*text + firstStart) != CR) firstStart--;
- firstStart++;
- while (start < textLen && *(*text + start) != CR) start++;
- if (start < textLen) {
- start++;
- } else if (firstStart > 0) {
- firstStart--;
- }
- len = start - firstStart;
- Munger(text, firstStart, nil, len, "", 0);
- }
-
-
-
- /*----------------------------------------------------------------------------
- ParseRe
-
- Parse the "Re:" portion of a followup subject header line.
-
- Entry: subject = pointer to subject header line.
- len = length of subject header line.
-
- Exit: function result = length of initial "Re:" of header line.
-
- The "Re:" portion of a subject line is the inital segment of the line
- which consists of any sequence of the following: "Re:", "Re(n):",
- "Re[n]:", and "Re^n:".
- ----------------------------------------------------------------------------*/
-
- long ParseRe (char *subject, long len)
- {
- char *p, *pEnd, *q;
- char closeBracket;
-
- p = subject;
- pEnd = p + len;
- while (p < pEnd) {
- q = p;
- if (q + 2 > pEnd) break;
- if (!MyStrNEqual(q, "Re", 2)) break;
- q += 2;
- while (q < pEnd && isLWSP(*q)) q++;
- if (q >= pEnd) break;
- if (*q == '(' || *q == '[') {
- closeBracket = *q == '(' ? ')' : ']';
- q++;
- while (q < pEnd && isLWSP(*q)) q++;
- while (q < pEnd && isdigit(*q)) q++;
- while (q < pEnd && isLWSP(*q)) q++;
- if (q >= pEnd) break;
- if (*q != closeBracket) break;
- q++;
- if (q >= pEnd) break;
- } else if (*q == '^') {
- q++;
- while (q < pEnd && isLWSP(*q)) q++;
- while (q < pEnd && isdigit(*q)) q++;
- if (q >= pEnd) break;
- }
- while (q < pEnd && isLWSP(*q)) q++;
- if (q >= pEnd) break;
- if (*q != ':') break;
- p = q + 1;
- while (p < pEnd && isLWSP(*p)) p++;
- }
- return p - subject;
- }
-
-
-
- /*----------------------------------------------------------------------------
- StringIsValidEmailAddress
-
- Check a string to see if it is a syntactically valid email address (a
- non-empty string, followed by '@', followed by a non-empty string,
- followed by '.', followed by a non-empty string).
-
- Entry: str = C-format string.
-
- Exit: function result = true if valid email address.
- ----------------------------------------------------------------------------*/
-
- Boolean StringIsValidEmailAddress (char *str)
- {
- char *p, *q;
-
- p = str;
- q = p;
- while (*q != 0 && *q != '@') q++;
- if (q == p || *q == 0) return false;
- p = q = q+1;
- while (*q != 0 && *q != '.') q++;
- if (q == p || *q == 0) return false;
- p = q = q+1;
- while (*q != 0) q++;
- return q > p;
- }